package main

import (
	"embed"
	"fmt"
	"log"
	"net"
	"net/http"
	"octopoda/children/games/wow"
	"os"
	"os/signal"
	"runtime"
	"sync"

	"github.com/astaxie/beego/logs"
	"github.com/ncruces/zenity"
	"github.com/spf13/viper"
	"github.com/zserge/lorca"
)

//go:embed www
var fs embed.FS

// Go types that are bound to the UI must be thread-safe, because each binding
// is executed in its own goroutine. In this simple case we may use atomic
// operations, but for more complex cases one should use proper synchronization.
type App struct {
	sync.Mutex
}

func (a *App) SaveToken(token string) {
	a.Lock()
	defer a.Unlock()
	viper.Set("token", token)
	viper.WriteConfig()
}

func (a *App) GetToken() string {
	a.Lock()
	defer a.Unlock()
	return viper.GetString("token")
}

func (a *App) SelectWowDir() string {
	a.Lock()
	defer a.Unlock()
	logs.Info("select file")
	filePath, err := zenity.SelectFile(zenity.FileFilters{
		{"exe", []string{"Wow.exe"}},
	})
	if err != nil {
		return err.Error()
	}
	viper.Set("wowDir", filePath)
	viper.WriteConfig()
	go wow.Start()
	return filePath
}

func (a *App) GetWowDir() string {
	a.Lock()
	defer a.Unlock()
	return viper.GetString("wowDir")
}

func init() {
	logs.SetLogger(logs.AdapterFile, `{"filename":"logs/octopoda.log","level":7,"daily":true,"color":true}`)
	logs.EnableFuncCallDepth(true)
	logs.SetLogFuncCall(true)

	viper.SetConfigName("config") // name of config file (without extension)
	viper.SetConfigType("yaml")   // REQUIRED if the config file does not have the extension in the name
	viper.AddConfigPath(".")      // optionally look for config in the working directory
	if _, err := os.Stat("config.yaml"); os.IsNotExist(err) {
		err = viper.SafeWriteConfig()
		if err != nil {
			logs.Error("SafeWriteConfig Fatal error config file: %s \n", err)
		}
	}

	err := viper.ReadInConfig() // Find and read the config file
	if err != nil {             // Handle errors reading the config file
		logs.Error("Fatal error config file: %s", err)
	}
}

func main() {
	go wow.Start()
	args := []string{}
	if runtime.GOOS == "linux" {
		args = append(args, "--class=Lorca")
	}
	ui, err := lorca.New("", "", 1024, 768, args...)
	if err != nil {
		log.Fatal(err)
	}
	defer ui.Close()

	// A simple way to know when UI is ready (uses body.onload event in JS)
	ui.Bind("start", func() {
		log.Println("UI is ready")
	})

	// Create and bind Go object to the UI
	a := &App{}
	ui.Bind("saveToken", a.SaveToken)
	ui.Bind("getToken", a.GetToken)
	ui.Bind("selectWowDir", a.SelectWowDir)
	ui.Bind("getWowDir", a.GetWowDir)

	// Load HTML.
	// You may also use `data:text/html,<base64>` approach to load initial HTML,
	// e.g: ui.Load("data:text/html," + url.PathEscape(html))

	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		log.Fatal(err)
	}
	defer ln.Close()
	go http.Serve(ln, http.FileServer(http.FS(fs)))

	if viper.GetBool("debug") {
		ui.Load("http://localhost:7006")
	} else {
		ui.Load(fmt.Sprintf("http://%s/www", ln.Addr()))
	}

	// You may use console.log to debug your JS code, it will be printed via
	// log.Println(). Also exceptions are printed in a similar manner.
	ui.Eval(`
		console.log("Hello, Octopoda!");
	`)

	// Wait until the interrupt signal arrives or browser window is closed
	sigc := make(chan os.Signal)
	signal.Notify(sigc, os.Interrupt)
	select {
	case <-sigc:
	case <-ui.Done():
	}

	log.Println("exiting...")
}
